![]() |
![]() |
|
Diese Aussage muss man sich auf der Zunge zergehen lassen: Die .NET-Zeitmessung beginnt am 1.1.0001 um 0:00 Uhr, und es können Intervalle von 0.0000001 Sekunden (in Worten: ein Zehnmillionstel) unterschieden werden. Die Anzahl der Ticks seit Beginn dieser Zeitrechnung können in einem long gespeichert werden, der groß genug ist, um damit alle Intervalle bis zum Ende des Jahres 9999 abdecken zu können. Wenn Sie das aktuelle Datum und die aktuelle Zeit ausgedrückt in Ticks interessiert, dann geben Sie den folgenden Code ein:
Zuerst rufen Sie die statische Methode Now auf, die das aktuelle Systemdatum einschließlich der Zeit als DateTime zurückliefert. Darauf wird die Eigenschaft Ticks angewendet, die Ihnen eine kaum noch fehlerfrei abschreibbare Zahl liefert. Umgekehrt können Sie natürlich auch aus einem long, der als Tick interpretiert werden soll, Datum und die Zeit ermitteln. Dazu wird die Klasse DateTime instanziiert und dabei ein Konstruktor aufgerufen, der einen long erwartet, der als die Anzahl der Ticks seit Beginn der .NET-Zeitrechnung interpretiert wird.
Die Ausgabe lautet:
Sie haben jetzt einen ersten Eindruck in den Umgang mit Datum und Uhrzeit gewonnen. Wir wollen uns nun die Details der Klasse DateTime ansehen. 10.4.2 Die Konstruktoren von »DateTime«
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| public DateTime (long ticks); |
Da in den meisten Fällen vermutlich kaum die Anzahl der Tick-Intervalle bekannt sein dürfte, kommt diesem Konstruktor geringe Bedeutung zu.
Wollen Sie mit einem bestimmten Datum operieren, bei dem die Uhrzeit keine Rolle spielt, bietet sich der folgende Konstruktor an:
| public DateTime (int year, int month, int day); |
Den Parametern wird das Jahr, der Monat und der Tag jeweils als int übergeben. Dabei ist die Jahresangabe auf die Zahlen zwischen einschließlich 1 und 9999 begrenzt, die Monatsangabe natürlich auf 1 bis 12, und die Anzahl der Tage ist abhängig vom Kalendermonat. Da bei diesem Konstruktor auf die Zeitangabe verzichtet wird, bezieht sich das Datum immer auf Mitternacht, also exakt 0:00 Uhr.
Im folgenden Beispiel setzen wir diesen Konstruktor ein, um das Datum 12. Mai 1965 abzubilden, und lassen uns an der Konsole die zu dieser Angabe entsprechenden Ticks ausgeben:
| DateTime myDate = new DateTime(1965, 5, 12); |
| Console.WriteLine("Datum: {0}", myDate); |
| Console.WriteLine("Ticks: {0}", myDate.Ticks); |
Für eine Präzisierung durch eine zusätzliche Zeitangabe stehen zwei Konstruktoren zur Verfügung. Während der erste zusätzlich zu der Angabe des Datums in drei weiteren Parametern Stunde, Minute und Sekunde entgegennimmt, kann dem anderen in einem siebten Parameter die Anzahl der Millisekunden übergeben werden:
| public DateTime (int year, int month, int day, int h, int min, int sec); |
| public DateTime (int year, int month, int day, int h, int min, int sec, int msec); |
Für jeden dieser Konstruktoren gibt es noch eine weitere Überladung, die um eine Referenz vom Typ System.Globalization.Calendar ergänzt ist. Damit kann man gegebenenfalls das Kalenderformat vom gregorianischen Kalender auf einen anderen umschalten.
Mit einer Neuerung wartet .NET 2.0 auf: Sie können nun auch festlegen, auf welcher Basis die Zeitangabe erfolgt. Dazu gibt es drei Möglichkeiten:
| Die Zeitangabe bezieht sich auf die lokale Systemzeit. |
| Die Zeitangabe legt die koordinierte Weltzeit (UTC) zugrunde. |
| Die Zeitangabe ist nicht genau spezifiziert. |
Die Festlegung muss bereits bei der Instanziierung erfolgen. Dazu stehen einige Konstruktoren zur Verfügung, denen ein Argument vom Typ DateTimeKind übergeben wird. Bei DateTimeKind handelt es sich um eine Enumeration mit den Elementen Local, Unspecified und Utc. Der Standard ist Unspecified.
| public DateTime (long ticks, DateTimeKind kind); |
Liegt ein DateTime-Objekt vor, lässt sich die Zeitbasis nicht mehr verändern. Sie können die Einstellung jedoch mit der Eigenschaft Kind jederzeit auswerten.
Liegt ein gültiges Datum vor, lassen sich daraus vom Jahr bis zu den Millisekunden sämtliche Informationen extrahieren. Die Namen der Eigenschaften sind sehr einprägsam: Year, Month, Day, Hour, Minute, Second und Millisecond.
Nehmen wir an, wir wollen das aktuelle Systemdatum, das wir mit der Eigenschaft Now ermitteln können, in seine Detailinformationen zerlegen, könnte das wie nachfolgend gezeigt erfolgen.
| DateTime newDate = DateTime.Now; |
| Console.WriteLine("Jahr = {0}", newDate.Year); |
| Console.WriteLine("Monat = {0}", newDate.Month); |
| Console.Write("Tag = {0}", newDate.Day); |
| Console.WriteLine(" ({0})", newDate.DayOfWeek.ToString()); |
| Console.WriteLine("Stunde = {0}", newDate.Hour); |
| Console.WriteLine("Minute = {0}", newDate.Minute); |
| Console.WriteLine("Sekunde = {0}", newDate.Second); |
| Console.WriteLine("MilliSek. = {0}", newDate.Millisecond); |
Weil die Ausgabe der Eigenschaft Day nur das aktuelle Tagesdatum liefert, ohne den Wochentag anzugeben, bietet sich eine weitere Eigenschaft von DateTime an, die aus einem gegebenen Datum sogar den Wochentag ermittelt: DayOfWeek. Der Rückgabewert ist vom Typ DayOfWeek, einer Enumeration des System-Namespace, die für den Sonntag 0 festlegt, für Montag 1 usw. Mit dem Aufruf von ToString auf DayOfWeek erhalten wir die der Konstanten entsprechende Zeichenfolge.
Bedauerlich ist, dass es keine gleichwertige Auflistung für die Monate gibt. Wenn Sie daher auch den Monatsnamen in der Ausgabe als Text und nicht nur als Zahl lesen wollen, müssen Sie die Zahlen 1–12 selbst den Monatsnamen zuordnen, beispielsweise mittels eines switch-Statements.
Now liefert die aktuelle Systemzeit und hängt damit unter Windows-Plattformen von der Einstellung in der Systemsteuerung ab. Eine sehr ähnliche Eigenschaft ist UtcNow, die aus den Einstellungen in der Systemsteuerung die Greenwich Mean Time (GMT) ermittelt.
Wenn auf die Zeitangabe, die uns Now liefert, verzichtet werden kann oder muss, können Sie auch auf die statische Eigenschaft Today zurückgreifen, die das Datum ohne Zeitangabe liefert. Diese wird auf 00:00:00 gesetzt.
Nahezu alle Methoden lassen sich in zwei operativen Gruppen zusammenfassen:
| Methoden, die der Ausgabeformatierung dienen |
| die Gruppe der Add-Methoden, mit der zu einem gegebenen Datum eine Zeitspanne addiert oder subtrahiert werden kann. |
Mit insgesamt vier vordefinierten Methoden lässt sich die Textausgabe eines DateTime-Objekts verändern:
| ToLongDateString und ToShortDateString |
| ToLongTimeString und ToShortTimeString |
Wir können den Effekt dieser Methoden am besten anhand von Beispielen sehen. Dazu deklarieren wir eine Variable vom Typ DateTime und führen darauf alle vier Methoden aus:
| // Datum = 3.Februar 2006 5:25:30 |
| DateTime myDate = new DateTime(2006, 2, 3, 5, 25, 30); |
| Console.WriteLine(myDate.ToLongDateString()); |
| // Ausgabe: Mittwoch, 3. Februar 2006 |
| Console.WriteLine(myDate.ToShortDateString()); |
| // Ausgabe: 03.02.2006 |
| Console.WriteLine(myDate.ToLongTimeString()); |
| // Ausgabe: 05:25:30 |
| Console.WriteLine(myDate.ToShortTimeString()); |
| // Ausgabe: 05:25 |
Weitaus interessanter als diese Formatmethoden ist eine andere, die ihre Leistungsfähigkeit erst auf den zweiten Blick offenbart: ToFileTime.
| public long ToFileTime(); |
Der Rückgabewert vom Typ long enthält die Anzahl der Ticks seit dem 1. Januar 1601, 12 Uhr. Welchen Hintergrund diese scheinbar zufällige Jahreszahl hat, verrät uns die Dokumentation nicht. Das soll uns aber auch nicht weiter interessieren, viel wichtiger ist die Tatsache, mit dieser Methode ein Mittel in den Händen zu halten, das uns ermöglicht, die Zeitspanne zwischen zwei Ereignissen zu messen – und dafür brauchen wir einen präzise definierten, gleichermaßen gültigen Ursprung, egal wie er definiert ist.
Im folgenden Beispiel steht die Methode ToFileTime im Mittelpunkt der Anwendung. Es ist ein kleines Spielchen, bei dem es darum geht, nach dem Starten der Anwendung ein bestimmtes Zeitintervall zu schätzen. Der Start des Spiels wird an der Konsole angezeigt. Wenn der Anwender meint, das von ihm vorher bekannt gegebene Zeitintervall sei abgelaufen, drückt er die (Enter)-Taste.
Weicht die Schätzung weniger als 10 % vom erforderlichen Intervall ab, gilt die Runde als gewonnen, ansonsten muss sich der als Verlierer erkannte Anwender vom Programm verhöhnen lassen.
| // ------------------------------------------------------------- |
| // Beispiel: ...\Kapitel 10\Zeitschätzung |
| // ------------------------------------------------------------- |
| class Program { |
| static void Main(string[] args) { |
| string str = "Hier können Sie Ihr Vermögen testen,"; |
| str += " eine Zeit zu schätzen.\n"; |
| str += "Wie viele Sekunden wollen Sie schätzen?"; |
| Console.WriteLine(str); |
| Console.Write("Zahl = "); |
| // Eingabe der zu schätzenden Sekundenanzahl |
| int numberOfSeconds = NumberTip.GetNumberInput(); |
| Console.WriteLine(new string('-', 60)); |
| Console.Write("Zum Starten und Beenden die "); |
| Console.WriteLine("Enter-Taste drücken."); |
| // Start des Anwenders |
| DateTime startTime = NumberTip.StartGame(); |
| // Beenden des Anwenders |
| DateTime endTime = NumberTip.EndGame(); |
| // Berechnung der Differenz |
| double dblTip = NumberTip.GetResult(startTime,endTime); |
| // Konsolenausgabe des Ergebnisses |
| Console.WriteLine(new string('=', 60)); |
| Console.Write("Ihr Tippergebnis: "); |
| Console.WriteLine("{0:N} sec.", dblTip); |
| if((dblTip < 0.9 * numberOfSeconds) || (dblTip > 1.1 * numberOfSeconds)) |
| Console.WriteLine("You are the greatest looser!"); |
| else |
| Console.WriteLine("The WINNER!"); |
| Console.ReadLine(); |
| } |
| } |
| public class NumberTip { |
| // Sekundenwahl des Anwenders |
| public static int GetNumberInput() { |
| return Convert.ToInt32(Console.ReadLine()); |
| } |
| // Starten des Spiels |
| public static DateTime StartGame() { |
| Console.ReadLine(); |
| Console.Write("START .................."); |
| return DateTime.Now; |
| } |
| // Spiel beenden |
| public static DateTime EndGame() { |
| Console.ReadLine(); |
| Console.WriteLine("....................ENDE"); |
| return DateTime.Now; |
| } |
| // Ergebnis ermitteln / t1 = Startzeit / t2 = Endzeit |
| public static double GetResult(DateTime t1, DateTime t2) { |
| return (t2.ToFileTime() – t1.ToFileTime()) / (double)TimeSpan.TicksPerSecond; |
| } |
| } |
Die Struktur der gesamten Anwendung ist so aufgebaut, dass sämtliche Funktionalitäten als statische Methoden in einer separaten Klasse (NumberTip) enthalten sind. Um eine Zeitdifferenz messen zu können, benötigen wir eine Startzeit und eine Endzeit. Die Startzeit muss festgehalten werden, wenn der Anwender die Messung startet. Hier hilft uns die Methode ToFileTime. Wird nämlich ein DateTime-Objekt erzeugt, enthält dieses immer die Anzahl der Ticks seit dem 1. Januar 1601 – gewissermaßen wie ein Geburtsdatum.
Also müssen wir im Code nach dem Start der Schätzung nur ein DateTime-Objekt erzeugen und ein zweites nach dem Beenden der Spielrunde. Durch einfache Subtraktion der beiden ToFileTime-Informationen erhalten wir die Anzahl der Ticks, die für die Auswertung maßgeblich sind:
| return (t2.ToFileTime() – t1.ToFileTime()) / (double)TimeSpan.TicksPerSecond; |
Um die tatsächliche Zeitspanne in Sekunden zu messen, dividieren wir das Ergebnis der Subtraktion nur noch durch die Anzahl der Ticks pro Sekunde. Hierbei ist eine andere Klasse behilflich – TimeSpan. Diese Klasse veröffentlicht unter anderem eine Konstante, die uns den passenden Divisor bereitstellt. Später werden wir uns diese Klasse noch etwas genauer ansehen.
Die Add-Methoden decken alle Bedürfnisse hinsichtlich der Änderung eines Datums oder einer Zeit ab. Aus dem Bezeichner ist sofort zu erkennen, welche Einheit zu einem DateTime-Objekt addiert werden soll:
| AddDays |
| AddHours |
| AddMonths |
| ... |
Allen Methoden wird ein Wert vom Typ int bzw. double übergeben, der Rückgabewert ist vom Typ DateTime, z.B.:
| public DateTime AddSeconds(double); |
Im folgenden Codefragment wird ein DateTime-Objekt erzeugt, welches das Datum 2. August 1995 und die Zeit 23:00 Uhr beschreibt. Davon werden mit der Methode AddHours 30 Stunden subtrahiert:
| DateTime now = new DateTime(1995, 8, 2, 23, 0, 0); |
| now = now.AddHours(-30); |
| Console.WriteLine(now); |
An der Konsole wird
| 01.08.1995 17:00:00 |
ausgegeben, was beweist, dass die Methode automatisch auch ein aus der Addition resultierendes neues Datum berücksichtigt.
Aus dem Pool der Methoden zur Manipulation einer DateTime-Instanz ragt eine heraus, deren Parametertyp sich von denen der anderen, gleichartigen Methoden unterscheidet. Es ist die Methode Add, deren Definition wir uns anschauen wollen:
| public DateTime Add(TimeSpan); |
Dem Parameter wird ein Objekt vom Typ TimeSpan übergeben. Dieser Typ scheint nur auf den ersten Blick dem Typ DateTime zu ähneln, aber beide unterscheiden sich grundlegend: Mit DateTime wird ein definitives Datum beschrieben, mit TimeSpan eine Zeitspanne. Dies machen auch die Konstruktoren der TimeSpan-Klasse deutlich, die Tage, Sunden, Minuten usw. entgegennehmen.
Der folgende Beispielcode zeigt, wie eine Referenz auf einer TimeSpan-Instanz an die Methode Add übergeben wird:
| DateTime now = new DateTime(2002, 2, 3, 12, 0, 0); |
| TimeSpan mySpan = new TimeSpan(3, 12, 15); |
| now = now.Add(mySpan); |
| Console.WriteLine(now); |
In der ersten Codezeile wird die Klasse DateTime instanziiert und dem Konstruktor das Datum 3. Februar 2002 12:00 Uhr übergeben. In der folgenden Anweisung erzeugen wir das TimeSpan-Objekt mySpan. Der Konstruktor mit drei Parametern übernimmt als Argumente Stunden, Minuten und Sekunden – er beschreibt demnach eine Zeitspanne von drei Stunden, zwölf Minuten, 15 Sekunden. Der Add-Methode wird danach das TimeSpan-Objekt übergeben, mit der Datum und Zeit neu bestimmt werden.
Eine Reihe vordefinierter Konstanten erleichtert die Umrechnung von Zeiteinheiten in Ticks, wenn unterschiedliche Zeitangaben mittels eines Faktors auf eine gemeinsame Einheitsbasis gebracht werden müssen – diese Basis bilden bekanntlich die Ticks. So beschreibt die Konstante TicksPerDay beispielsweise die Anzahl der Ticks pro Tag, TicksPerHour die Ticks pro Stunde.
Das folgende Beispiel zeigt, wie diese Konstanten sinnvoll eingesetzt werden können. Die benutzerdefinierten Funktionen DiffHours und DiffSeconds nehmen jeweils eine Referenz vom Typ DateTime entgegen und berechnen den Unterschied zwischen zwei Datumswerten in Stunden bzw. Sekunden. Dabei ist das erste das aktuelle Systemdatum, das wie üblich über Now ermittelt wird, das zweite muss der Anwender an der Konsole eingeben.
| // ------------------------------------------------------------- |
| // Beispiel: ...\Kapitel 10\Zeitdifferenz |
| // ------------------------------------------------------------- |
| class Class1 { |
| static void Main(string[] args) { |
| // aktuelle Systemzeit festlegen |
| DateTime actDate = DateTime.Now; |
| // das zu vergleichende Datum entgegennehmen |
| Console.Write("Geben Sie das Vergleichsdatum "); |
| Console.Write("im Format tt.mm.jjjj ein: "); |
| string strDate = Convert.ToString(Console.ReadLine()); |
| // die Eingabe passend formatieren |
| strDate = strDate.Replace('.', '/'); |
| DateTime newDate = Convert.ToDateTime(strDate); |
| // Ausgabe der Differenz in Stunden |
| Console.Write("Die Differenz in Stunden: "); |
| Console.WriteLine(DiffHours(actDate, newDate)); |
| // Ausgabe der Differenz in Sekunden |
| Console.Write("Die Differenz in Sekunden: "); |
| Console.WriteLine(DiffSeconds(actDate, newDate)); |
| Console.ReadLine(); |
| } |
| public static long DiffHours(DateTime d1,DateTime d2) { |
| long x = d2.Ticks – d1.Ticks; |
| return Convert.ToInt64(x / TimeSpan.TicksPerHour); |
| } |
| public static long DiffSeconds(DateTime d1,DateTime d2) { |
| long x = d2.Ticks – d1.Ticks; |
| return Convert.ToInt64(x / TimeSpan.TicksPerSecond); |
| } |
| } |
Das Eingabeformat ist vorgeschrieben, da es nach der Bestätigung durch den Aufruf der Methode in ein Format überführt wird, das einem DateTime-Typ entspricht.
Der Algorithmus des Programmcodes in den beiden Methoden DiffHours und DiffSeconds ist ähnlich. Beide Routinen nehmen in ihren Parametern Referenzen des Typs DateTime entgegen, rechnen das jeweilige Datum mit der Eigenschaft Ticks zunächst in die Basiseinheit um, um im Anschluss daran die Differenz zu bilden. Die abschließende Division durch TicksPerHour bzw. TicksPerSecond liefert einen long, der dem Aufrufer zurückgegeben wird.
Eine TimeSpan-Instanz beschreibt eine Zeitspanne aus Tagen, Stunden, Minuten, Sekunden und Millisekunden. Diese Zeitspanne lässt sich in einem String abbilden, der dem folgenden Format entspricht:
| Tag.Stunden:Minuten:Sekunden.Sekundenbruchteil |
Dazu ein Beispiel. Mit
| TimeSpan myTimeSpan = new TimeSpan(2, 12, 30, 22, 100); |
| Console.WriteLine(a.ToString()); |
erhalten Sie die folgende Ausgabe:
| 2.12:30:22.1000000 |
Manchmal ist es notwendig, die Angabe einer Zeitspanne in eine konkrete Zeiteinheit umzurechnen oder aus der Angabe einer Zahl, die eine Zeiteinheit widerspiegelt, ein TimeSpan-Objekt zu erzeugen. Auch dazu liefert die Klasse TimeSpan die passenden Methoden. Nehmen wir an, Sie wollten das mit
| TimeSpan span = new TimeSpan(1, 35, 45); |
erzeugte Objekt, das einen Tag, 35 Minuten und 45 Sekunden beschreibt, in eine Zahl umwandeln. Es stellt sich mit den Möglichkeiten der Klasse nur noch die Frage, ob Sie eine Ganzzahl oder eine Dezimalzahl benötigen. Für beide Fälle gibt es die passenden Eigenschaften. Beispielsweise liefert:
| die Hours-Eigenschaft einen int |
| die TotalHours-Eigenschaft einen double |
Die Anweisung
| Console.WriteLine(span.TotalHours); |
wird das Ergebnis 1,5958333333333 haben, beim Aufruf von Hours wird der Dezimalteil abgeschnitten. Analog lautende Eigenschaften gibt es auch, um mit Tagen, Minuten oder Sekunden zu operieren.
Einige statische Methoden des Typs TimeSpan ermöglichen auch ohne Initialisierung, einer TimeSpan-Variablen einen Wert zuzuweisen. Diese Methoden beginnen mit dem Präfix From, beispielsweise FromSeconds, FromMinutes, FromDays.
| // die Anweisung beschreibt eine Zeitspanne von 3 Stunden |
| TimeSpan ts = TimeSpan.FromHours(3); |
| << zurück |
|
||||||||||||||
|
||||||||||||||
|
||||||||||||||
|
||||||||||||||
Copyright © Galileo Press 2006
Für Ihren privaten Gebrauch dürfen Sie die Online-Version natürlich ausdrucken. Ansonsten unterliegt das <openbook> denselben Bestimmungen, wie die gebundene Ausgabe: Das Werk einschließlich aller seiner Teile ist urheberrechtlich geschützt. Alle Rechte vorbehalten einschließlich der Vervielfältigung, Übersetzung, Mikroverfilmung sowie Einspeicherung und Verarbeitung in elektronischen Systemen.